package com.libleveldb.implementation.mock

import android.util.Log
import com.libleveldb.Iterator
import com.libleveldb.LevelDB
import com.libleveldb.Snapshot
import com.libleveldb.WriteBatch
import com.libleveldb.exception.LevelDBClosedException
import com.libleveldb.exception.LevelDBException
import com.libleveldb.exception.LevelDBSnapshotOwnershipException
import com.libleveldb.util.Bytes
import java.util.*

open class MockLevelDB : LevelDB() {
    @Volatile
    override var isClosed = false

    @JvmField
    val map: SortedMap<ByteArray, ByteArray> = TreeMap(Bytes.COMPARATOR)
    override fun close() {
        var multipleClose = false
        synchronized(this) {
            if (isClosed) {
                multipleClose = true
            } else {
                isClosed = true
            }
        }
        if (multipleClose) {
            Log.i(MockLevelDB::class.java.name, "Trying to close Mock LevelDB multiple times.")
        }
    }

    @Synchronized
    @Throws(LevelDBException::class)
    override fun put(key: ByteArray?, value: ByteArray?, sync: Boolean) {
        if (value == null) {
            del(key, sync)
            return
        }
        requireNotNull(key) { "Key must not be null." }
        checkIfClosed()
        map[key] = value
    }

    @Synchronized
    @Throws(LevelDBException::class)
    override fun write(writeBatch: WriteBatch?, sync: Boolean) {
        requireNotNull(writeBatch) { "Write batch must not be null." }
        checkIfClosed()
        for (operation in writeBatch.allOperations!!) {
            if (operation!!.isDel) {
                map.remove(operation.key())
            } else {
                map[operation.key()] = operation.value()
            }
        }
    }

    @Throws(LevelDBSnapshotOwnershipException::class, LevelDBException::class)
    override fun get(key: ByteArray?, snapshot: Snapshot?): ByteArray {
        requireNotNull(key) { "Key must not be null." }
        if (snapshot != null) {
            if (snapshot !is MockSnapshot) {
                throw LevelDBSnapshotOwnershipException()
            }
            if (!snapshot.checkOwnership(this)) {
                throw LevelDBSnapshotOwnershipException()
            }
        }
        synchronized(this) {
            checkIfClosed()
            return if (snapshot != null) {
                (snapshot as MockSnapshot).snapshot?.get(key)!!
            } else map[key]!!
        }
    }

    @Synchronized
    @Throws(LevelDBException::class)
    override fun del(key: ByteArray?, sync: Boolean) {
        requireNotNull(key) { "Key must not be null." }
        checkIfClosed()
        map.remove(key)
    }

    @Throws(LevelDBClosedException::class)
    override fun getPropertyBytes(key: ByteArray?): ByteArray? {
        throw UnsupportedOperationException("Mock LevelDB does not support properties.")
    }

    @Throws(LevelDBSnapshotOwnershipException::class, LevelDBClosedException::class)
    override fun iterator(fillCache: Boolean, snapshot: Snapshot?): Iterator {
        if (snapshot != null) {
            if (snapshot !is MockSnapshot) {
                throw LevelDBSnapshotOwnershipException()
            }
            if (!snapshot.checkOwnership(this)) {
                throw LevelDBSnapshotOwnershipException()
            }
            return snapshot.snapshot?.let { MockIterator(it) }
                ?: synchronized(this) { return MockIterator(map) }
        }
        synchronized(this) { return MockIterator(map) }
    }

    @Synchronized
    @Throws(LevelDBClosedException::class)
    override fun iterator(fillCache: Boolean): Iterator {
        return MockIterator(map)
    }

    // No-op.
    override var path: String?
        get() = ":MOCK:"
        set(_) {
        }

    @Throws(LevelDBClosedException::class)
    override fun obtainSnapshot(): Snapshot {
        return MockSnapshot(this)
    }

    @Throws(LevelDBSnapshotOwnershipException::class, LevelDBClosedException::class)
    override fun releaseSnapshot(snapshot: Snapshot?) {
        requireNotNull(snapshot) { "Snapshot must not be null." }
        if (snapshot !is MockSnapshot) {
            throw LevelDBSnapshotOwnershipException()
        }
        if (!snapshot.checkOwnership(this)) {
            throw LevelDBSnapshotOwnershipException()
        }
        synchronized(this) {
            checkIfClosed()
            snapshot.release()
        }
    }

    @Throws(LevelDBClosedException::class)
    protected fun checkIfClosed() {
        if (isClosed) {
            throw LevelDBClosedException("Mock LevelDB has been closed.")
        }
    }
}